#ifndef BHXJGLMODEL_H
#define BHXJGLMODEL_H
#include<vector>
#include "bhxjglinclude.h"
#include "bhxjglshader.h"
#include<stb/stb_image.h>
#include<memory>
/* struct BHXJGLVertex
 * 冰河洗剑 2020.4.16
 * 面法线 有些情况下 细节粗糙
 * 增加法线成员,着色程序自行决定使用顶点法线或者面法线
 *
 * 冰河洗剑 2020.4.5
 * 自定义顶点结构体
 * 注意该顶点数据中不包括法线数据
*/
struct BHXJCONTROLS_EXPORT BHXJGLVertex
{
    ///坐标
    glm::vec3 position;
    ///法线
    glm::vec3 normal;
    ///纹理坐标
    glm::vec2 texcoodr;    
};



/* class BHXJGLTexture2D
 * 冰河洗剑 2020.4.5
 * 2D纹理
 *
*/
class BHXJCONTROLS_EXPORT BHXJGLTexture2D
{
public:
    BHXJGLTexture2D();
    virtual ~BHXJGLTexture2D();
    void Init(BHXJGLFUN *glcore, const std::string &Path);
    void Use(unsigned int index);

protected:

    BHXJGLFUN *gl=nullptr;
    unsigned int textID=0;

};





/* class BHXJGLMesh
 *  冰河洗剑 2020.4.30
 * 还是顶点法线过渡性更好，尽可能Mesh生成时候就计算出顶点的法线
 *
 *  冰河洗剑 2020.4.15-1
 * 彻底放弃绘制三角形时候传入法线参数，改由几何着色器计算法线，性能大幅度提升。
 * 删除了法线和法线索引成员。
 *
 *  冰河洗剑 2020.4.15
 * 去掉mesh中的shader成员，增加Paint函数ishader参数来指定shader程序
 * 增加Paint函数 passinnormal参数来确定是否向shader程序传入法线(normal)数据
 *
 *  冰河洗剑 2020.4.5
 * 网格数据类
 *
 * vertices顶点数据
 * indices 索引数据
 * normals法线数据
 * normalindices法线索引,每个三角形对应一个法线，所以法线索引是顶点索引的1/3，和网格中的三角形数量相等
*/
class BHXJCONTROLS_EXPORT BHXJGLMesh
{
public:
    BHXJGLMesh();
    virtual ~BHXJGLMesh();

    ///顶点
    std::vector<BHXJGLVertex> vertices;
    ///索引
    std::vector<unsigned int> indices;
    ///三角形数量
    unsigned int trianglescount{0};
    ///纹理
    std::vector<BHXJGLTexture2D*> textures;
    ///初始化
    void Init(BHXJGLFUN *glcore);
    ///绘制
    void Paint(BHXJGLShader *ishader);
protected:
    BHXJGLFUN *gl=nullptr;
    unsigned int VAO, VBO, EBO;
};


/* class BHXJGLPaintParam
 * 冰河洗剑 2020.4.15
 * 并不是所有情况下利用该类传递参数绘制都会获得便利
 *
 *
 * 冰河洗剑 2020.4.2
 * 绘制参数
 * 1.Up Right Size Position为model相关属性，会影响Model矩阵的结果
 * 1.可以通过SetUp setRight setSize setPosition函数来确定Model矩阵
 *   compute参数为true会重新计算model矩阵
 *   也可以再设置完上述属性后调用setModel函数来重新计算Model矩阵
 * 2.rotateModel为模型矩阵旋转函数
 *   该函数会重新计算Up Right属性和Model矩阵，不会影响Size Position
 *   anew=true 以mat4(1)为基底旋转
 *   anew=false 以现有Model为基底进行旋转
*/
class BHXJCONTROLS_EXPORT BHXJGLPaintParam
{
public:
    BHXJGLPaintParam();
    virtual ~BHXJGLPaintParam();

    const glm::mat4& Projection()const{return _Projection;}
    void setProjection(const glm::mat4 &value){_Projection=value;}

    const glm::mat4& View()const{return _View;}
    void setView(const glm::mat4 &value){_View=value;}

    const glm::vec3& Up()const{return _Up;}
    void setUp(const glm::vec3 &up,bool compute=false){_Up=up;if(compute)setModel();}

    const glm::vec3& Right()const{return _Right;}
    void setRight(const glm::vec3 &right,bool compute=false){_Right=right;if(compute)setModel();}

    const glm::vec3& Size()const{return _Size;}
    void setSize(const glm::vec3 &value,bool compute=false){_Size=value;if(compute)setModel();}
    void setSize(const glm::vec2 &value,bool compute=false){_Size=glm::vec3(value,1);if(compute)setModel();}
    void setSize(const float &x,const float &y,const float &z ,bool compute=false)
    {_Size=glm::vec3(x,y,z);if(compute)setModel();}

    const glm::vec3& Position()const{return _Position;}
    void setPosition(const glm::vec3 &value,bool compute=false){_Position=value;if(compute)setModel();}
    void setPosition(const float &x,const float &y,const float &z ,bool compute=false)
    {_Position=glm::vec3(x,y,z);if(compute)setModel();}


    const glm::mat4& Model()const;
    void setModel();
    void rotateModel(const glm::vec3 &vec,const float &angle,bool anew);
protected:
    glm::mat4 _Projection{1};
    glm::mat4 _View{1};
    glm::vec3 _Up{0,1,0};
    glm::vec3 _Right{1,0,0};
    glm::vec3 _Size{1};
    glm::vec3 _Position{0};
    glm::mat4 _Model{1};
};




/* class BHXJGLModel
 * 冰河洗剑 2020.4.30
 * 将RegularShape中的mesh索引添加函数挪到该模板类中
 *
 * 冰河洗剑 2020.4.15
 * 增加Paint函数重载来适应不同的使用需求
 *
 * 冰河洗剑 2020.4.5
 * 模型模板类
 * meshes网格数据
 * textures纹理数据
*/

template<class... Args>
class BHXJGLModel
{
#define BHXJFront true
#define BHXJBack false

    ///逆时针
#define BHXJCCW true
    ///顺时针
#define BHXJCW  false

public:
    BHXJGLModel()
    {

    }
    virtual ~BHXJGLModel()
    {
        for(BHXJGLMesh *m: meshes)
        {
            delete m;
        }
        for(BHXJGLTexture2D *t: textures)
        {
            delete t;
        }
    }
    virtual void Init(Args... arg)=0;

    virtual void Paint(const BHXJGLPaintParam &p)
    {
        BHXJGLShader* shader;
        if(shaders.size()>0)
        {
            shader=shaders[0];
            shader->use();
            shader->setMat4("projection",p.Projection());
            shader->setMat4("view",p.View());
            shader->setMat4("model",p.Model());
        }
        if(shader==nullptr)
            return;
        for(BHXJGLMesh *bh:meshes)
        {
            bh->Paint(shader);
        }
    }

    virtual void Paint(const BHXJGLPaintParam &p,BHXJGLShader *ishader)
    {
        BHXJGLShader *shader=nullptr;
        if(ishader!=nullptr)
            shader=ishader;
        else
        {
            if(shaders.size()>0)
            {
                shader=shaders[0];
            }
        }
        if(shader==nullptr)
            return;

        shader->use();
        shader->setMat4("projection",p.Projection());
        shader->setMat4("view",p.View());
        shader->setMat4("model",p.Model());
        for(BHXJGLMesh *bh:meshes)
        {
            bh->Paint(shader);
        }
    }

    virtual void Paint(const glm::mat4 &projection,const glm::mat4 &view,const glm::mat4 &model,BHXJGLShader *ishader=nullptr)
    {
        BHXJGLShader *shader=nullptr;
        if(ishader!=nullptr)
            shader=ishader;
        else
        {
            if(shaders.size()>0)
            {
                shader=shaders[0];
            }
        }
        if(shader==nullptr)
            return;

        shader->use();
        shader->setMat4("projection",projection);
        shader->setMat4("view",view);
        shader->setMat4("model",model);
        for(BHXJGLMesh *bh:meshes)
        {
            bh->Paint(shader);
        }
    }


protected:
    std::vector<BHXJGLMesh*> meshes;
    BHXJGLFUN *gl=nullptr;

    std::vector<BHXJGLShader*> shaders;

    ///所有mesh的纹理都要存到这里,model释放时统一释放
    std::vector<BHXJGLTexture2D*> textures;


};

class BHXJCONTROLS_EXPORT BHXJGLModelHelper
{
public:
    ///方形点群
    ///(-1,1,0);
    ///(-1,-1,0);
    ///(1,-1,0);
    ///(1,1,0);
    static std::unique_ptr<std::vector<glm::vec3>>  getQuadPoints(bool clockwise=BHXJCCW)
    {
        std::unique_ptr<std::vector<glm::vec3>> ret(new std::vector<glm::vec3>());
        ret->push_back(glm::vec3(-1,1,0));
        ret->push_back(glm::vec3(-1,-1,0));
        ret->push_back(glm::vec3(1,-1,0));
        ret->push_back(glm::vec3(1,1,0));
        if(clockwise==BHXJCW)
        {
            std::reverse(ret->begin(),ret->end());
        }
        return  ret;
    }


    ///生成线点群(-1,0,0)到(1,0,0)
    static std::unique_ptr<std::vector<glm::vec3>> getLinePoints(unsigned segnum)
    {
        std::unique_ptr<std::vector<glm::vec3>> ret(new std::vector<glm::vec3>());
        for(unsigned int i=0;i<segnum+1;++i)
        {
            ret->push_back(glm::vec3(-1.f+i*(2.0f/segnum),0,0));
        }
        return ret;
    }

    ///生成圆形点群（默认点逆时针排列）
    static std::unique_ptr<std::vector<glm::vec3>> getCirclePoints(unsigned int pointnum,bool clockwise=BHXJCCW)
    {
        using namespace glm;
        float radian;
        if(clockwise==BHXJCW)
            radian=glm::radians(360.f);
        else
            radian=glm::radians(-360.f);
        std::unique_ptr<std::vector<glm::vec3>> ret(new std::vector<glm::vec3>());
        float step=radian/pointnum;
        for (unsigned int i=0;i<pointnum;++i)
        {
            float r=step*i;
            ret->push_back(vec3(glm::sin(r),glm::cos(r),0));
        }
        ret->push_back(glm::vec3(0,1,0));
        return  ret;
    }

    ///生成圆弧点群（默认点顺时针排列）
    static std::unique_ptr<std::vector<glm::vec3>> getArcPoints(unsigned int intervalnum,float angle,bool clockwise=BHXJCW)
    {
        using namespace glm;
        float radian;
        if(clockwise==BHXJCW)
            radian=glm::radians(angle);
        else
            radian=glm::radians(-angle);
        std::unique_ptr<std::vector<glm::vec3>> ret(new std::vector<glm::vec3>());
        float step=radian/intervalnum;
        for (unsigned int i=0;i<intervalnum+1;++i)
        {
            float r=step*i;
            ret->push_back(vec3(glm::sin(r),glm::cos(r),0));
        }
        return  ret;
    }

    ///添加方形
    static void addQuadToMesh(BHXJGLMesh *m,unsigned int offset, bool front=BHXJFront)
    {

        unsigned int i0=offset;
        unsigned int i1=offset+1;
        unsigned int i2=offset+2;
        unsigned int i3=offset+3;


        if(front==BHXJFront)
        {
            m->indices.push_back(i0);
            m->indices.push_back(i1);
            m->indices.push_back(i2);

            m->indices.push_back(i0);
            m->indices.push_back(i2);
            m->indices.push_back(i3);
        }
        else
        {
            m->indices.push_back(i0);
            m->indices.push_back(i2);
            m->indices.push_back(i1);

            m->indices.push_back(i0);
            m->indices.push_back(i3);
            m->indices.push_back(i2);
        }
        m->trianglescount+=2;
    }

    ///添加条状物体
    static void addStripeToMesh(BHXJGLMesh *m,unsigned int arcpointoffset0,unsigned int arcpointoffset1,unsigned num,bool clockwise=BHXJCCW)
    {
        for(unsigned int i=0;i<num;++i)
        {
            unsigned int i0=arcpointoffset0+i;
            unsigned int i00=arcpointoffset1+i;
            unsigned int i1=arcpointoffset0+i+1;
            unsigned int i11=arcpointoffset1+i+1;


            if(BHXJCCW==clockwise)
            {
                m->indices.push_back(i0);
                m->indices.push_back(i00);
                m->indices.push_back(i11);

                m->indices.push_back(i0);
                m->indices.push_back(i11);
                m->indices.push_back(i1);
            }
            else
            {
                m->indices.push_back(i0);
                m->indices.push_back(i11);
                m->indices.push_back(i00);

                m->indices.push_back(i0);
                m->indices.push_back(i1);
                m->indices.push_back(i11);
            }
            m->trianglescount+=2;
        }
    }

    ///添加圆锥侧面索引（不管锥底）
    static void addConeToMesh(BHXJGLMesh *m,unsigned int arcpointoffset0,unsigned int arcpointoffset1,unsigned num,bool front=BHXJFront)
    {
        if(num<3)
            return;
        //侧面面三角形
        for(unsigned int i=0;i<num;++i)
        {
            unsigned int i0=arcpointoffset0+i;
            unsigned int i00=arcpointoffset1+i;
            unsigned int i11=arcpointoffset1+i+1;

            if(front!=BHXJFront)
                std::swap(i11,i00);

            m->indices.push_back(i0);
            m->indices.push_back(i00);
            m->indices.push_back(i11);

            m->trianglescount+=1;
        }
    }

    ///添加圆面索引（逆时针为正面）
    static void addPieToMesh(BHXJGLMesh *m,unsigned int heartid,unsigned int arcpointoffset,unsigned num,bool front=BHXJFront)
    {
        if(num<3)
            return;
        if(front==BHXJFront)
        {
            for(unsigned int i=0;i<num;++i)
            {
                //索引
                m->indices.push_back(heartid);
                m->indices.push_back(arcpointoffset+i);
                m->indices.push_back(arcpointoffset+i+1);
                m->trianglescount+=1;
            }
        }
        else
        {
            for(unsigned int i=0;i<num;++i)
            {
                //索引
                m->indices.push_back(heartid);
                m->indices.push_back(arcpointoffset+i+1);
                m->indices.push_back(arcpointoffset+i);
                m->trianglescount+=1;
            }
        }
    }

    ///测试二维多边形的时针顺序
    static bool testClockWise(const std::vector<glm::vec2> &points,unsigned int count=0)
    {
        unsigned int id=0;
        unsigned int idprevious=0;
        unsigned int idnext=0;
        unsigned int num=count==0?points.size():count;
        float ytemp=points.front().y;
        for(unsigned int i=0;i<num;++i)
        {
            id=points[i].y>ytemp?i:id;
        }

        idprevious=id==0?num-1:id-1;
        idnext=id==(num-1)?0:id+1;

        glm::vec3 p(points[id].x,points[id].y,0);
        glm::vec3 pn(points[idnext].x,points[idnext].y,0);
        glm::vec3 pp(points[idprevious].x,points[idprevious].y,0);

        glm::vec3 normal= glm::cross(pn-p,pp-p);

        if(normal.z>0)
            return BHXJCCW;
        else
            return BHXJCW;
    }

};
#endif // BHXJGLMODEL_H
